﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Framework
{
    /// <summary>
    /// 从ab中加载具体的引擎资源
    /// </summary>
    public class AssetBundleLoader : MonoSingleton<AssetBundleLoader>
    {
        /// <summary>
        /// 最多同时加载的数量
        /// </summary>
        private int maxCount;
        /// <summary>
        /// 当前正在加载的数量
        /// </summary>
        private int curCount;
        private Queue<AssetFromAbTask> allLoadTasks;

        public override void Init(System.Action cb = null)
        {
            base.Init(cb);
            maxCount = 16;
            curCount = 0;
            allLoadTasks = new Queue<AssetFromAbTask>();
        }

        public void SetMaxLoadingCount(int count)
        {
            maxCount = count;
        }

        /// <summary>
        /// 正在加载
        /// </summary>
        /// <returns></returns>
        internal bool IsLoading => curCount > 0;

        /// <summary>
        /// 从ab中加载引擎资源对象
        /// </summary>
        /// <param name="assetItem">资源</param>
        /// <param name="onLoaded">加载完成之后的回调</param>
        internal void PreLoadAssetFromAb(AssetItem assetItem, System.Action<AssetItem, bool> onLoaded)
        {
            if (null != assetItem.EngineAsset)
            {
                if (null == onLoaded)
                {
                    return;
                }
                try
                {
                    onLoaded(assetItem, true);
                }
                catch (System.Exception exp)
                {
                    Logger.LogError("AssetBundleLoader.LoadAssetFromAb, exp = " + exp);
                    Logger.LogException(exp);
                }
            }
            else
            {
                allLoadTasks.Enqueue(new AssetFromAbTask(assetItem, onLoaded));
                StartCoroutine(StartLoading());
            }
        }

        /// <summary>
        /// 异步从ab中加载资源
        /// </summary>
        /// <param name="assetItem"></param>
        /// <param name="loadedCb"></param>
        /// <param name="resName"></param>
        /// <param name="resType"></param>
        public void LoadAssetFromAb(AssetItem assetItem, System.Action<AssetItem, UnityEngine.Object> loadedCb, string resName = null, System.Type resType = null)
        {
            if (null != assetItem.EngineAsset)
            {
                loadedCb(assetItem, assetItem.EngineAsset);
            }
            else
            {
                allLoadTasks.Enqueue(new AssetFromAbTask(assetItem, null, loadedCb, resName, resType));
                StartCoroutine(StartLoading());
            }
        }

        private IEnumerator StartLoading()
        {
            if (allLoadTasks.Count <= 0 || curCount >= maxCount)
            {
                Logger.Log("AssetBundleLoader.StartLoading, trigger max loading count limit! curCount = " + curCount + " maxCount = " + maxCount);
                yield return null;
            }
            AssetFromAbTask newTask = allLoadTasks.Dequeue();
            curCount++;
            if (newTask.assetItem.NeedLoadAssetFromAb)
            {
                yield return StartCoroutine(_StartPreLoadAsset(newTask));
            }
            else
            {
                yield return StartCoroutine(StartLoadAsset(newTask));
            }
            curCount--;
        }

        /// <summary>
        /// 开始预加载, 预加载只处理一个ab中有单个引擎对象的资源单位
        /// </summary>
        /// <param name="task">加载任务</param>
        /// <returns></returns>
        private IEnumerator _StartPreLoadAsset(AssetFromAbTask task)
        {
            if (!task.assetItem.AbAsset)
            {
                Logger.LogWarning("AssetBundleLoader._StartPreLoadAsset, task.assetItem.AbAsset = null!");
                task.OnTaskDone();
                yield break;
            }

            AssetItem assetItem = task.assetItem;
            string[] allAssetNames = assetItem.AbAsset.GetAllAssetNames();
            if (allAssetNames.Length == 1)
            {
                //没有被其他资源引用, 并且其引擎对象资源没有加载出来
                int abRefCount = ResMgr.Instance.GetAbRefCount(assetItem.AssetUrl);
                //Logger.Log("AssetBundleLoader._StartPreLoadAsset, ab refCount = " + abRefCount + " assetItem = " + assetItem);
                if (abRefCount < 2 && (null == assetItem.EngineAsset))
                {
                    while (null != assetItem.loadAssetFromAbAction)
                    {
                        yield return null;
                    }

                    string assetName = allAssetNames[0];
                    assetItem.loadAssetFromAbAction = assetItem.AbAsset.LoadAssetAsync(assetName);
                    yield return assetItem.loadAssetFromAbAction;

                    if (assetItem.loadAssetFromAbAction.isDone)
                    {
                        assetItem.SetEngineAssetFromAb(assetItem.loadAssetFromAbAction.asset);
                        assetItem.loadAssetFromAbAction = null;
                        if (abRefCount < 1)
                        {
                            // Logger.Log("AssetBundleLoader._StartPreLoadAsset, 预加载成功了， assetName = " + assetName + " abRefCount = " + abRefCount);
                            //如果不是音频文件，则卸载其ab
                            assetItem.UnLoadAssetBundle();
                        }
                    }
                }
            }
            task.OnTaskDone();
        }

        /// <summary>
        /// 开始异步加载
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        private IEnumerator StartLoadAsset(AssetFromAbTask task)
        {
            AssetItem assetItem = task.assetItem;
            //ab没有加载完成
            if (!assetItem.IsLoadSuccess)
            {
                yield break;
            }

            //循环等待所有的依赖加载成功
            if (null != assetItem.allDependencies)
            {
                int depCout = assetItem.allDependencies.Count;
                for (int idx = 0; idx < depCout; idx++)
                {
                    if (!assetItem.allDependencies[idx].IsLoadFinish)
                    {
                        yield return null;
                        idx--;
                    }
                }
            }
            //如果有正在加载的操作，则等待这个操作完成
            while (null != assetItem.loadAssetFromAbAction)
            {
                yield return null;
            }
            //如果ab中的资源没有加载出来，则开始加载
            if (assetItem.AbAsset && null == assetItem.EngineAsset)
            {
                string[] allAssetNames = assetItem.AbAsset.GetAllAssetNames();
                if (allAssetNames.Length > 1)
                {
                    Logger.LogError("AssetBundleLoader._StartLoadAsset, assetItem = " + assetItem + " 不应该有两个asset打到一个assetbundle!");
                }
                if (string.IsNullOrEmpty(task.assetName))
                {
                    string assetName = FrameworkSettings.GetAssetNameInBundle(assetItem.AssetUrl);
                    assetItem.loadAssetFromAbAction = assetItem.AbAsset.LoadAssetAsync(assetName);
                    yield return assetItem.loadAssetFromAbAction;
                    AssetBundleRequest tmpRequest = assetItem.loadAssetFromAbAction;
                    if (tmpRequest.isDone)
                    {
                        assetItem.SetEngineAssetFromAb(tmpRequest.asset);
                    }
                    assetItem.loadAssetFromAbAction = null;
                }
                else
                {
                    string assetName = FrameworkSettings.GetAssetNameInBundle(task.assetName);
                    if (null == task.assetType)
                    {
                        assetItem.loadAssetFromAbAction = assetItem.AbAsset.LoadAssetAsync(assetName);
                        yield return assetItem.loadAssetFromAbAction;
                    }
                    else
                    {
                        assetItem.loadAssetFromAbAction = assetItem.AbAsset.LoadAssetAsync(assetName, task.assetType);
                        yield return assetItem.loadAssetFromAbAction;
                    }
                    if (assetItem.loadAssetFromAbAction.isDone)
                    {
                        assetItem.SetEngineAssetFromAb(assetItem.loadAssetFromAbAction.asset);
                    }
                    assetItem.loadAssetFromAbAction = null;
                }
                //没有别人引用了，则卸载掉ab占用的内存
                int refCount = ResMgr.Instance.GetAbRefCount(assetItem.AssetUrl);
                if (refCount < 1)
                {
                    assetItem.UnLoadAssetBundle();
                }
            }
            task.OnTaskDone();
        }

        private sealed class AssetFromAbTask
        {
            /// <summary>
            /// 存放ab的地方
            /// </summary>
            internal AssetItem assetItem;
            internal System.Action<AssetItem, bool> onAssetLoaded;
            internal System.Action<AssetItem, UnityEngine.Object> onAssetObjLoaded;
            internal string assetName;
            internal System.Type assetType;

            /// <summary>
            /// 构建一个从ab中加载引擎对象的任务
            /// </summary>
            /// <param name="assetItem">ab对象存放的资源</param>
            /// <param name="assetCb">资源回调</param> 
            /// <param name="assetObjCb">引擎对象回调</param>
            /// <param name="assetName">引擎资源名字</param>
            /// <param name="objType">引擎资源对象类型</param>
            internal AssetFromAbTask(AssetItem assetItem, System.Action<AssetItem, bool> assetCb = null,
                System.Action<AssetItem, UnityEngine.Object> assetObjCb = null, string assetName = null, System.Type objType = null)
            {
                this.assetItem = assetItem;
                this.onAssetLoaded = assetCb;
                this.onAssetObjLoaded = assetObjCb;
                this.assetName = assetName;
                this.assetType = objType;
            }

            internal void OnTaskDone()
            {
                if (null != onAssetLoaded)
                {
                    try
                    {
                        onAssetLoaded(assetItem, true);
                    }
                    catch (System.Exception exp)
                    {
                        Logger.LogError("AssetFromAbTask.OnTaskDone, call onAssetLoaded exception! assetItem = " + assetItem);
                        Logger.LogException(exp);
                    }
                }
                if (null != onAssetObjLoaded)
                {
                    try
                    {
                        onAssetObjLoaded(assetItem, assetItem.EngineAsset);
                    }
                    catch (System.Exception exp)
                    {
                        Logger.LogError("AssetFromAbTask.OnTaskDone, call onAssetObjLoaded exception!");
                        Logger.LogException(exp);
                    }
                }
            }

            internal void Destroy()
            {
                assetItem = null;
                onAssetLoaded = null;
                onAssetObjLoaded = null;
                assetName = null;
                assetType = null;
            }
        }
    }
}
